package org.locker.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.service.WxPayService;
import lombok.RequiredArgsConstructor;
import lombok.Synchronized;
import org.locker.common.core.exception.ServiceException;
import org.locker.common.core.utils.MapstructUtils;
import org.locker.common.core.utils.StringUtils;
import org.locker.common.mybatis.core.page.PageQuery;
import org.locker.domain.Order;
import org.locker.domain.User;
import org.locker.domain.Withdraw;
import org.locker.domain.bo.WithdrawBo;
import org.locker.domain.enums.OrderRefundStatusEnum;
import org.locker.domain.enums.OrderStatusEnum;
import org.locker.domain.enums.WithdrawStatusEnum;
import org.locker.domain.vo.WithdrawVo;
import org.locker.mapper.WithdrawMapper;
import org.locker.service.IOrderService;
import org.locker.service.IUserService;
import org.locker.service.IWithdrawService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 提现Service业务层处理
 *
 * @author winnie
 * @date 2023-10-11
 */
@RequiredArgsConstructor
@Service
public class WithdrawServiceImpl extends ServiceImpl<WithdrawMapper, Withdraw> implements IWithdrawService {

    private final WithdrawMapper baseMapper;
    private final IUserService userService;
    private final WxPayService wxPayService;
    private final IOrderService orderService;

    /**
     * 查询提现
     */
    @Override
    public WithdrawVo queryById(Long withdrawId) {
        return baseMapper.selectVoById(withdrawId);
    }

    /**
     * 查询提现列表
     */
    @Override
    public Page<WithdrawVo> queryPageList(WithdrawBo bo, PageQuery pageQuery) {
        return baseMapper.getList(pageQuery.build(), bo);
    }

    /**
     * 查询提现列表
     */
    @Override
    public List<WithdrawVo> queryList(WithdrawBo bo) {
        LambdaQueryWrapper<Withdraw> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<Withdraw> buildQueryWrapper(WithdrawBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<Withdraw> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserId() != null, Withdraw::getUserId, bo.getUserId());
        lqw.eq(StringUtils.isNotBlank(bo.getWithdrawNo()), Withdraw::getWithdrawNo, bo.getWithdrawNo());
        lqw.eq(StringUtils.isNotBlank(bo.getTransactionNo()), Withdraw::getTransactionNo, bo.getTransactionNo());
        lqw.eq(bo.getAmount() != null, Withdraw::getAmount, bo.getAmount());
        lqw.eq(bo.getWithdrawTime() != null, Withdraw::getWithdrawTime, bo.getWithdrawTime());
        lqw.eq(StringUtils.isNotBlank(bo.getWithdrawStatus()), Withdraw::getWithdrawStatus, bo.getWithdrawStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getWithdrawRemark()), Withdraw::getWithdrawRemark, bo.getWithdrawRemark());
        return lqw;
    }

    /**
     * 新增提现
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Synchronized
    public Long insertByBo(WithdrawBo bo) {
        //提现金额
        BigDecimal amount = bo.getAmount();
        Long userId = bo.getUserId();
        User user = userService.getById(userId);
        BigDecimal balanceAmount = user.getBalanceAmount();
        if (amount.compareTo(balanceAmount) == 1) {
            throw new ServiceException(StrUtil.format("提现金额:{}大于用户余额:{}!", amount, balanceAmount));
        }
        //用户账户的 余额扣减操作
        BigDecimal subtract = balanceAmount.subtract(amount);
        BigDecimal freezeAmount = user.getFreezeAmount().add(amount);
        boolean flag = userService.update(Wrappers.<User>lambdaUpdate()
            .set(User::getBalanceAmount, subtract)
            .set(User::getFreezeAmount, freezeAmount)
            .eq(User::getUserId, userId)
            .eq(User::getVersion, user.getVersion()));
        if (!flag) {
            throw new ServiceException("提醒失败,请重试!");
        }
        Withdraw withdraw = new Withdraw();
        withdraw.setAmount(amount);
        withdraw.setUserId(userId);
        withdraw.setWithdrawStatus(WithdrawStatusEnum.WITHDRAWING.getCode());
        withdraw.setWithdrawNo(IdUtil.getSnowflake().nextIdStr());
        baseMapper.insert(withdraw);
        //todo 触发微信提现
        return withdraw.getWithdrawId();
    }

    /**
     * 修改提现
     */
    @Override
    public Boolean updateByBo(WithdrawBo bo) {
        Withdraw update = MapstructUtils.convert(bo, Withdraw.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(Withdraw entity) {
        //做一些数据校验,如唯一约束
    }

    /**
     * 批量删除提现
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long withdrawV2(Long userId, String orderNo, String withDrawType) {
        User user = userService.getOne(Wrappers.<User>lambdaQuery()
            .eq(User::getUserId, userId).eq(User::getLatestOrderNo, orderNo));
        Order order = orderService.getOne(Wrappers.<Order>lambdaQuery()
            .eq(Order::getUserId, userId).eq(Order::getOrderNo, orderNo));
        String outRefundNo = IdUtil.getSnowflake().nextIdStr();

        if (ObjectUtil.isNotNull(user) && ObjectUtil.isNotNull(order) && !order.getOrderNo().equals(OrderRefundStatusEnum.REFUND_SUCCESS.getCode())) {
            WxPayRefundRequest request = new WxPayRefundRequest();
            request.setTransactionId(order.getTransactionNo());
            request.setOutRefundNo(outRefundNo);
            request.setTotalFee(BaseWxPayRequest.yuanToFen(StrUtil.toString(order.getPayAmount())));
            request.setRefundFee(BaseWxPayRequest.yuanToFen(StrUtil.toString(user.getBalanceAmount())));
            WxPayRefundResult refund;
            try {
                refund = wxPayService.refund(request);
            } catch (Exception e) {
                log.error(StrUtil.format("退款失败!userId:{}, orderNo:{}, 自定义退款号:{}", userId, orderNo, outRefundNo), e);
                throw new RuntimeException("目前提现人数过多,请1分钟后再重试。");
            }

            //修改订单的退款状态
            orderService.update(Wrappers.<Order>lambdaUpdate()
                .set(Order::getRefundStatus, OrderRefundStatusEnum.REFUND_SUCCESS.getCode()).eq(Order::getOrderNo, orderNo));
            //创建提线记录
            Withdraw withdraw = new Withdraw();
            withdraw.setWithdrawNo(outRefundNo);
            withdraw.setWithdrawTime(DateUtil.date());
            withdraw.setTransactionNo(refund.getTransactionId());
            withdraw.setWithdrawStatus(WithdrawStatusEnum.WITHDRAW_SUCCESS.getCode());
            withdraw.setUserId(userId);
            withdraw.setAmount(user.getBalanceAmount());
            withdraw.setOrderNo(orderNo);
            withdraw.setWithdrawType(withDrawType);
            this.save(withdraw);


            //重新设置用户余额信息
            setUserBalance(userId, orderNo);

            return withdraw.getWithdrawId();
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long withdrawBySystem(Long userId, String orderNo, String withDrawType) {
        Order order = orderService.getOne(Wrappers.<Order>lambdaQuery()
            .eq(Order::getUserId, userId).eq(Order::getOrderNo, orderNo));
        String outRefundNo = IdUtil.getSnowflake().nextIdStr();
        if (ObjectUtil.isNull(order)) {
            throw new ServiceException(StrUtil.format("订单号:{}不存在", orderNo));
        }
        if (!OrderRefundStatusEnum.REFUND_NO.getCode().equals(order.getRefundStatus()) || !OrderStatusEnum.USED_DONE.getCode().equals(order.getOrderStatus())) {
            throw new ServiceException(StrUtil.format("订单号:{}所处状态不能提现", orderNo));
        }
        if (BigDecimal.ZERO.compareTo(order.getResidueAmount()) == 0 || BigDecimal.ZERO.compareTo(order.getResidueAmount()) == 1) {
            throw new ServiceException(StrUtil.format("订单号:{}余额为0", orderNo));
        }
        WxPayRefundRequest request = new WxPayRefundRequest();
        request.setTransactionId(order.getTransactionNo());
        request.setOutRefundNo(outRefundNo);
        request.setTotalFee(BaseWxPayRequest.yuanToFen(StrUtil.toString(order.getPayAmount())));
        request.setRefundFee(BaseWxPayRequest.yuanToFen(StrUtil.toString(order.getResidueAmount())));
        WxPayRefundResult refund;
        try {
            refund = wxPayService.refund(request);
        } catch (Exception e) {
            log.error(StrUtil.format("退款失败!userId:{}, orderNo:{}, 自定义退款号:{}", userId, orderNo, outRefundNo), e);
            throw new RuntimeException("提现失败，请联系管理员");
        }
        //修改订单的退款状态
        orderService.update(Wrappers.<Order>lambdaUpdate()
            .set(Order::getRefundStatus, OrderRefundStatusEnum.REFUND_SUCCESS.getCode()).eq(Order::getOrderNo, orderNo));


        //创建提线记录
        Withdraw withdraw = new Withdraw();
        withdraw.setWithdrawNo(outRefundNo);
        withdraw.setWithdrawTime(DateUtil.date());
        withdraw.setTransactionNo(refund.getTransactionId());
        withdraw.setWithdrawStatus(WithdrawStatusEnum.WITHDRAW_SUCCESS.getCode());
        withdraw.setUserId(userId);
        withdraw.setAmount(order.getResidueAmount());
        withdraw.setOrderNo(orderNo);
        withdraw.setWithdrawType(withDrawType);
        this.save(withdraw);

        //重新设置用户余额信息
        setUserBalance(userId, orderNo);

        return withdraw.getWithdrawId();
    }

    @Transactional
    public void setUserBalance(Long userId, String orderNo) {
        //已结束待退款的  订单余额是按当前最新订单退款押金显示，如果您存在多笔押金未提现，请在本次提现完再进入该页面进行上一次押金的提现。
//        List<Order> list = orderService.list(Wrappers.<Order>lambdaQuery()
//            .eq(Order::getOrderStatus, OrderStatusEnum.USED_DONE.getCode())
//            .eq(Order::getUserId,userId)
//            .ne(Order::getOrderNo,orderNo)
//            .ne(Order::getOrderType, OrderTypeEnum.FREE.getCode())
//            .eq(Order::getRefundStatus, OrderRefundStatusEnum.REFUND_NO.getCode())
//            .orderByDesc(BaseEntity::getCreateTime));
//        if (CollUtil.isNotEmpty(list)) {
//            Order order = list.get(0);
//            userService.update(Wrappers.<User>lambdaUpdate()
//                .set(User::getBalanceAmount,order.getResidueAmount())
//                .set(User::getLatestOrderNo,order.getOrderNo())
//                .eq(User::getUserId,order.getUserId()));
//        }else {
//            userService.update(Wrappers.<User>lambdaUpdate().set(User::getBalanceAmount,BigDecimal.ZERO)
//                .set(User::getLatestOrderNo,"").eq(User::getUserId,userId));
//        }

        // 新要求：余额显示那，用户有多笔要退的，只显示最新的那一笔，退了以后就显示为0，用户要退就找客服后台操作。
        userService.update(Wrappers.<User>lambdaUpdate().set(User::getBalanceAmount, BigDecimal.ZERO)
            .set(User::getLatestOrderNo, "").eq(User::getUserId, userId));

    }
}
